package com.fair.concurrency;

import java.util.concurrent.locks.StampedLock;

public class Point {
    // 点的坐标
    private double x, y;
    private final StampedLock sl = new StampedLock();

    /**
     * 独占锁 方法 (写锁)
     * 用来移动一个点的坐标
     *
     * @param deltaX
     * @param deltaY
     */
    void move(double deltaX, double deltaY) {
        // 获得写锁
        long stamp = sl.writeLock();
        try {
            x += deltaX;
            y += deltaY;
        } finally {
            // 释放写锁
            sl.unlockWrite(stamp);
        }
    }

    /**
     * 读锁方法
     * 计算这个点到圆点的距离
     * @return
     */
    double distanceFromOrigin() {
        // 获得一个乐观读锁
        long stamp = sl.tryOptimisticRead();
        double currentX = x, currentY = y;
        // 验证获得乐观苏之后，是否有发生写操作
        // true 表示没有修改过，false 表示已经修改过
        // 需要获取悲观读锁（等待锁释放）
        if (!sl.validate(stamp)) {
            // 如果已经发生过些操作，则使用悲观读锁重读数据
            stamp = sl.readLock();
            try {
                currentX = x;
                currentY = y;
            } finally {
                // 读完数据之后，释放悲观读锁
                sl.unlockRead(stamp);
            }
        }
        return Math.sqrt(currentX * currentX + currentY * currentY);
    }

    /**
     * 从乐观读锁 升级为 写锁
     * 当这个点在圆点的时候，移动该坐标
     *
     * @param newX
     * @param newY
     */
    void moveIfAtOrigin(double newX, double newY) {
        long stamp = sl.readLock();
        try {
            while (x == 0.0 && y == 0.0) {
                // 从读锁转换为写锁
                long ws = sl.tryConvertToWriteLock(stamp);
                // 如果失败则返回 0 ，非 0 表示成功
                if (ws != 0L) {
                    stamp = ws;
                    x = newX;
                    y = newY;
                    break;
                } else {
                    sl.unlockRead(stamp);
                    stamp = sl.writeLock();
                }
            }
        } finally {
            sl.unlock(stamp);
        }
    }
}
